﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace memcached.Tx
{
    /// <summary>
    /// After enlistment, the resource manager responds to the transaction's requests.
    /// </summary>
    internal sealed class TxCacheResourceManager : BaseResourceManager
    {
        /// <summary>
        /// Timeout to gain the system-wide lock for the transaction.
        /// </summary>
        public static int DefaultSyncTimeout = 1000;

        public ICacheAdvanced Temporary = new MemCacheMock();

        private readonly string id;

        private readonly List<ITxCacheOperation> _journal = new List<ITxCacheOperation>(2);

        private Action<string> cleanupAction;

        public TxCacheResourceManager(System.Transactions.Transaction tx)
        {
            id = Environment.MachineName;// system-wide lock name
            tx.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None);
        }

        public object EnlistOperation(ITxCacheOperation operation)
        {
#if DEBUG
            System.Diagnostics.Trace.TraceInformation("Executing {0}", operation.Name);
#endif
            //System.Diagnostics.Debug.Assert(cleanupAction != null);
            object res = operation.Execute();
            _journal.Add(operation);
            return res;
        }

        public void OnCleanup(Action<string> cleanup)
        {
            cleanupAction = cleanup;
        }

        protected override void Commit()
        {
            System.Diagnostics.Debug.Assert(ChangesMade);
            // ensure only one commit/rollback at a time happens
            DistMemCache.PerformCrossProcess(1000, id, () =>
            {
                for (int i = 0; i < _journal.Count; i++)
                {
                    ITxCacheOperation op = _journal[i];
#if DEBUG
                    System.Diagnostics.Trace.TraceInformation("Commit {0}", op.Name);
#endif
                    if (!op.Persist())
                        throw new ApplicationException(string.Format("Operation {0} failed to Commit.", op.Name));
                }
                cleanupAction(id);// remove reference to this instance so that GC will clear resources
                cleanupAction = null;
            });
            base.Commit();
        }

        protected override void Rollback()
        {
            System.Diagnostics.Debug.Assert(ChangesMade);
            // ensure only one commit/rollback at a time happens
            DistMemCache.PerformCrossProcess(1000, id, () =>
            {
                for (int i = _journal.Count - 1; i > 0; i--)
                {
                    ITxCacheOperation op = _journal[i];
#if DEBUG
                    System.Diagnostics.Trace.TraceInformation("Rollback {0}", op.Name);
#endif
                    if (!op.Rollback())
                        throw new ApplicationException(string.Format("Operation {0} failed to Rollback.", op.Name));
                }
                cleanupAction(id);
                cleanupAction = null;
            });
            base.Rollback();
        }

        protected override bool ChangesMade
        {
            get { return _journal.Count > 0; }
        }
    }
}